Skip to content

Add Phi-4 multimodal support#4648

Open
yicycyc wants to merge 4 commits into
PaddlePaddle:developfrom
yicycyc:feat/phi4-multimodal-migration
Open

Add Phi-4 multimodal support#4648
yicycyc wants to merge 4 commits into
PaddlePaddle:developfrom
yicycyc:feat/phi4-multimodal-migration

Conversation

@yicycyc

@yicycyc yicycyc commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

PR 新增模型支持:Phi-4-multimodal

本 PR 新增 microsoft/Phi-4-multimodal-instruct 支持。复现对齐对象为 Microsoft/HuggingFace remote-code 实现以及 ModelScope 上的原始 safetensors 权重。

主要改动

  • 新增 paddleformers/transformers/phi4_multimodal/
    • Phi4MultimodalConfig
    • Phi4MultimodalModel
    • Phi4MultimodalForCausalLM
    • Phi4MultimodalForCausalLMPipe
    • Phi4MultimodalProcessor
    • Phi4MultimodalImageProcessor
    • Phi4MultimodalFeatureExtractor
    • 文本 decoder、LongRoPE、RMSNorm、MLP、attention eager/cache generation
    • 视觉分支:ViT、patch embedding、vision attention、pooling head、image projector、image feature merge
    • 音频分支:audio feature extractor、Conformer encoder、audio projector、speech/vision-speech adapter route
  • 注册 Phi-4-multimodal 到:
    • paddleformers.transformers
    • AutoConfig
    • AutoModel
    • AutoModelForCausalLM
    • AutoProcessor
    • AutoImageProcessor
    • AutoFeatureExtractor
  • 新增 Phi-4-multimodal 多模态 SFT 输入链路:
    • 新增 phi4_multimodal template / MM plugin
    • mm_collate_fn 支持 image_pixel_valuesimage_sizesimage_attention_mask
    • mm_collate_fn 支持 audio_input_featuresaudio_attention_maskaudio_embed_sizes
    • 支持 input_mode,用于区分 text / vision / speech / vision-speech adapter route
    • SFT label mask 支持屏蔽 image/audio token
  • 权重加载:
    • Phi4MultimodalPreTrainedModel._gen_aoa_config 中实现 HF/ModelScope safetensors 到 PaddleFormers 权重的自动转换规则
    • 支持原始 phi4mm config 转 Paddle Phi4MultimodalConfig
    • 用户可直接使用原始 safetensors 目录加载,无需手动运行离线权重转换脚本
  • 文档:
    • 更新 README 模型列表
    • 更新 docs/zh/model_capability.md 能力矩阵

前向对齐验证

模型:microsoft/Phi-4-multimodal-instruct

环境设置:

  • Paddle: FLAGS_use_accuracy_compatible_kernel=1FLAGS_cudnn_deterministic=1NVIDIA_TF32_OVERRIDE=0
  • Torch: torch.backends.cudnn.deterministic=Truetorch.backends.cudnn.allow_tf32=False
  • attention backend:两侧均使用 eager

fp32 最终 logits:

模态 adapter HF top token Paddle top token logits max abs diff logits mean abs diff 结论
text none 1 1 0.0 0.0 对齐
vision vision 199999 199999 0.0001831055 0.0000245297 对齐
audio speech 11 11 0.0012969971 0.0002102744 对齐

text bf16 补充:

输入长度 logits max abs diff logits mean abs diff top token 是否一致
1 token 0.0 0.0
2 tokens 0.0 0.0
4 tokens 0.0 0.0

生成对齐

Text-only 生成:

dtype 输入 前 10 token step logits max abs diff step logits mean abs diff 结论
bf16 [[100, 101]] [220, 17, 87, 659, 220, 18, 88, 314, 220, 899] 0.0 0.0 对齐
fp32 [[100, 101]] [220, 17, 87, 659, 220, 18, 88, 314, 220, 899] 3.6239624e-05 2.6317712e-06 对齐

Vision 生成:

dtype 输入 前 10 token step logits max abs diff step logits mean abs diff 结论
bf16 synthetic real-shape image tokens, seq len 549 [200, 200, 200, 200, 200, 200, 200, 200, 200, 200] 0.0 0.0 对齐

Audio 说明:

  • audio bf16 生成前 10 token 已一致。
  • logits 仍存在差异,已定位为 Paddle/Torch bf16 Conv1D 算子差异。

训练验证

1. 文本

使用 GSM8K 做 BF16 full-SFT,Paddle 侧使用 sharding stage3,Torch 侧使用 ms-swift ZeRO-3。

共同设置:

  • global batch size = 4
  • learning_rate = 1e-5
  • warmup_steps = 0
  • weight_decay = 0
  • shuffle 关闭
  • attention eager

loss曲线如下:

phi4_gsm8k_len512_300_loss_curve_paddle_vs_transformers

2. 视觉多模态

使用小多模态 SFT 数据做 BF16 full-SFT,Paddle 侧使用 sharding stage3,Torch 侧使用 ms-swift ZeRO-3。

loss曲线如下:
phi4mm_tiny_vision_300_loss_curve_detailed

@paddle-bot

paddle-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown

Thanks for your contribution!

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已完成首轮检查,发现几处需要修复后再合入的问题,详情见行内评论。CI 目前仍有任务在运行,且 H20 集成测试出现失败/取消状态,建议作者后续一并确认最新 CI 结果。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

Comment on lines +104 to +106
("phi4_multimodal", "Phi4Multimodal"),
("phi4_multimodal_audio", "Phi4MultimodalAudio"),
("phi4_multimodal_vision", "Phi4MultimodalVision"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1
处理要求:请针对该评论修复并提交新的 commit。

这里把 AutoModelmodel_type 映射到了 Phi4Multimodal / Phi4MultimodalAudio / Phi4MultimodalVision,但新增的 modeling.py 里没有这些类;顶层 paddleformers.transformers.__init__ 也把 Phi4MultimodalAudioPreTrainedModelPhi4MultimodalVisionPreTrainedModel 等不存在的名字加入了 lazy import。_LazyAutoMapping 会按这里的名字执行 getattr,用户通过 AutoModel.from_config(...) 或顶层 import 访问这些符号时会直接失败,导致 PR 描述里的 Auto 注册不可用。

请把 Auto/lazy 注册统一到实际存在且可实例化的类名,或者补齐这些公开别名/包装类。修复形态可以类似:

# paddleformers/transformers/auto/configuration.py
("phi4_multimodal", "Phi4MultimodalModel")
# 如果 audio/vision 子配置也要暴露给 AutoModel,需要同时提供对应的 PretrainedModel 包装类;
# 否则不要把未定义的 Phi4MultimodalAudio / Phi4MultimodalVision 注册进 MODEL_NAMES_MAPPING。

同时请同步清理或补齐 paddleformers/transformers/__init__.pyphi4_multimodal.modeling 的 import structure,确保其中每个名字都能从新模块实际导入。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 bd71d2f 中修复。phi4_multimodal 现在映射到实际存在的 Phi4MultimodalModel;同时移除了不支持的 audio/vision 子配置 AutoModel 映射,并清理了顶层 lazy import 中不存在的类名。此外补充了 AutoModel 映射回归测试。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已确认,bd71d2f1 中的修复和你描述一致:当前 MODEL_NAMES_MAPPING 只保留 phi4_multimodal -> Phi4MultimodalModel,audio/vision 子配置不再注册到 AutoModel,顶层 lazy import 里不存在的类名也已清理,并补充了对应回归测试。这个问题已在当前 head 复查通过。

Comment on lines +1365 to +1366
if cls != cls.base_model_class:
stmts.append("model.embed_tokens.weight -> lm_head.weight")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1
处理要求:请针对该评论修复并提交新的 commit。

Phi4MultimodalConfig 默认 tie_word_embeddings=False,而原始 Phi-4 multimodal checkpoint 也包含独立的 lm_head.weight。这里无条件把 model.embed_tokens.weight 写到 lm_head.weight,直接加载原始 safetensors 时会用词表 embedding 覆盖输出头,导致 logits/生成结果错误。请按 tie_word_embeddings 分支加载:未 tie 时读取 checkpoint 里的 lm_head.weight

Suggested change
if cls != cls.base_model_class:
stmts.append("model.embed_tokens.weight -> lm_head.weight")
if cls != cls.base_model_class:
if config.tie_word_embeddings:
stmts.append("model.embed_tokens.weight -> lm_head.weight")
else:
stmts.append("lm_head.weight -> lm_head.weight")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 bd71d2f 中修复。现在会根据 tie_word_embeddings 选择权重映射:未绑定时加载 checkpoint 中独立的 lm_head.weight,绑定时才使用 model.embed_tokens.weight。同时补充了两种配置的回归测试。



def adaptive_enc_mask(x_len, chunk_start_idx, left_window=0, right_window=0):
import torch

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1
处理要求:请针对该评论修复并提交新的 commit。

adaptive_enc_mask 在音频 forward 路径里执行 import torch,但 PaddleFormers 默认会在导入时屏蔽 torch/torchvision,并且运行依赖也不能假设 Paddle-only 环境安装了 PyTorch。音频输入会经过 _streaming_mask -> adaptive_enc_mask,因此这里会在 speech/vision-speech 路径上触发 ModuleNotFoundError,使新增音频能力不可用。

请用 Paddle 张量操作重写这段 mask 构造,不要在模型 forward 里依赖 PyTorch。可参考下面的实现形态:

def adaptive_enc_mask(x_len, chunk_start_idx, left_window=0, right_window=0):
    chunk_start_idx_t = paddle.to_tensor(chunk_start_idx, dtype="int64")
    start_pad = paddle.concat([paddle.zeros([1], dtype="int64"), chunk_start_idx_t])
    end_pad = paddle.concat([chunk_start_idx_t, paddle.full([1], x_len, dtype="int64")])

    seq_range = paddle.arange(x_len, dtype="int64").unsqueeze(-1)
    idx = paddle.nonzero((seq_range < end_pad) & (seq_range >= start_pad))[:, 1]
    seq_range_expand = paddle.arange(x_len, dtype="int64").unsqueeze(0).expand([x_len, x_len])

    idx_left = paddle.clip(idx - left_window, min=0)
    idx_right = paddle.clip(idx + right_window, max=len(chunk_start_idx))
    boundary_left = paddle.gather(start_pad, idx_left)
    boundary_right = paddle.gather(end_pad, idx_right)
    return (seq_range_expand >= boundary_left.unsqueeze(-1)) & (seq_range_expand < boundary_right.unsqueeze(-1))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已在 bd71d2f 中修复。adaptive_enc_mask 已改为纯 Paddle 张量实现,音频 forward 路径不再依赖 PyTorch。同时补充了基础 chunk mask 和 left-window mask 的回归测试。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已确认,bd71d2f1 中的修复和你描述一致:当前 adaptive_enc_mask 已改为 paddle.to_tensor / paddle.concat / paddle.nonzero / paddle.gather 等 Paddle 实现,音频 forward 路径不再依赖 torch。新增 chunk mask 与 left-window mask 回归测试也覆盖了这部分逻辑。这个问题已在当前 head 复查通过。

@Paddle-CI-Bot

Paddle-CI-Bot commented Jul 15, 2026

Copy link
Copy Markdown

PaddleFormers Log Analysis

Run #29412994050 · Attempt 1

日志分析报告

流水线名称 问题标签 修复建议 日志片段
Integration test (H20, multi-card) nvidia-container-cli 初始化超时 与本PR无关,H20 runner GPU 驱动 RPC 不通,CI 维护人员检查机器 nvidia-container-runtime 状态,rerun 报错代码
Integration test (H20, single card) nvidia-container-cli 初始化超时 与本PR无关,H20 runner GPU 驱动 RPC 不通,CI 维护人员检查机器 nvidia-container-runtime 状态,rerun 报错代码

失败的测试case:

两个 job 均在 "Check docker image and run container" 阶段提前退出,exit code 125,
后续所有测试 step(GLM4.5/Qwen 等训练任务)均未执行,无具体 test case 失败。

根本原因分析:
两个 H20 runner(paddle-11-1567435 / paddle-5-1567435,同一物理机 yqlcc01-bbc-yqonlinea-com-1567435)的 nvidia-container-runtime 与宿主机 nvidia 驱动之间的 RPC 通信超时,导致 docker run --gpus 启动失败,与 PR #4648 代码改动无关。

修复建议:

  1. CI 维护人员登录 yqlcc01-bbc-yqonlinea-com-1567435,检查 nvidia-container-runtime 及 nvidia 驱动状态:systemctl status nvidia-persistenced / nvidia-smi
  2. 重启 nvidia-container-runtime 相关服务或重启宿主机驱动后,对 PR Add Phi-4 multimodal support #4648 重新触发 rerun。

🔍 准确性记录:请点击评论底部 😊 图标,选择 👍(准确)或 👎(有误),将自动记录到 CI 监控系统

🔄 每次 Re-run 后自动更新

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查新提交,之前的 P1 问题均已通过后续 commit 修复:Auto/lazy 注册已指向实际存在的类,lm_head AOA 映射已按 tie_word_embeddings 分支处理,音频 mask 构造也已移除 PyTorch 依赖。未发现新的需要阻塞合入的问题。

当前 CI 仍在运行中;我本地尝试运行新增的聚焦测试时,环境缺少 colorlog,因此测试结果请以后续线上 CI 为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants